package com.learn.temp.api;

import org.springframework.objenesis.instantiator.sun.UnsafeFactoryInstantiator;
import sun.misc.Unsafe;

import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;

class VolatileBarrierExample {
    int a;
    volatile int v1 = 1;
    volatile int v2 = 2;

    void readAndWrite() {
        int i = v1;// 第一个volatile读
        int j = v2;// 第二个volatile读
        a = i + j;// 普通写
        v1 = i + 1;// 第一个volatile写
        v2 = j * 2;// 第二个 volatile写
    }
}

/**
 * @author wangxing
 * @version 2020/6/5 16:10 Administrator
 */
public class VolatileReOrderSample {
    static int a, b, x, y, i;

    /**
     * 指令重排测试验证
     */
    public static void reOrder() throws InterruptedException {
        Map<String, Integer> d = new HashMap<>();
        while (true) {
            a = 0;
            b = 0;
            x = 0;
            y = 0;
            i++;
            Thread t1 = new Thread(() -> {
                a = 1;
                x = b;
            });
            Thread t2 = new Thread(() -> {
                b = 1;
                y = a;
            });
            t1.start();
            t2.start();
            t1.join();
            t2.join();
            String str = "输出x=" + x + ",y=" + y;
            if (d.containsKey(str)) {
                d.put(str, d.get(str) + 1);
            } else {
                d.put(str, 1);
            }
            System.out.println("循环" + i + "次," + str);
            if (x == 0 && y == 0) {
                //            只有当发生指令重排导致x=b和y=a排列到了a=1和b=1之前才会出现x=0和y=0的情况
                System.out.println(Stream.of(d).toString());
                return;
            } else {
                System.out.println();
            }
        }
    }

    /**
     * 处理指令重排的方式
     * 给涉及的内容加锁
     * 给涉及的内容加volatile关键字
     * volatile关键字防止指令重排的原理是内存屏障
     * 当第一个操作是volatile,第二个操作是volatile读时不能重排序
     * 在volatile写前的内存屏障是StoreStore,写之后的屏障是StoreLoad
     * 在volatile读前的内存屏障是StoreLoad,读之后的内存屏障是LoadLoad
     * 直接添加内存屏障使用Usafe
     * 具体实现方式为
     * Unsafe.getUnsafe().storeFence();
     * Unsafe.getUnsafe().loadFence();
     *
     * @throws InterruptedException
     */
    public static void reOrder2() throws InterruptedException {
        Map<String, Integer> d = new HashMap<>();
        while (true) {
            a = 0;
            b = 0;
            x = 0;
            y = 0;
            i++;
            Thread t1 = new Thread(() -> {
                a = 1;
                x = b;
            });
            Thread t2 = new Thread(() -> {
                b = 1;
                y = a;
            });
            t1.start();
            t2.start();
            t1.join();
            t2.join();
            String str = "输出x=" + x + ",y=" + y;
            if (d.containsKey(str)) {
                d.put(str, d.get(str) + 1);
            } else {
                d.put(str, 1);
            }
            System.out.println("循环" + i + "次," + str);
            if (x == 0 && y == 0) {
                //            只有当发生指令重排导致x=b和y=a排列到了a=1和b=1之前才会出现x=0和y=0的情况
                System.out.println(Stream.of(d).toString());
                return;
            } else {
                System.out.println();
            }
        }
    }

    public static void main(String[] args) {
        try {
            reOrder();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}
